﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Data;
using System.Collections.ObjectModel;
using System.Collections;
using System.ComponentModel;
using System.Collections.Specialized;
using System.Windows.Input;
using System.Windows.Controls.Primitives;

namespace Renci.iRods.Studio.Modules.Explorer.Controls
{
    public class TreeListView : ListView
    {
        #region Properties

        /// <summary>
        /// Internal collection of rows representing visible nodes, actually displayed in the ListView
        /// </summary>
        internal ObservableCollectionAdv<TreeListViewNode> Rows
        {
            get;
            private set;
        }

        public IEnumerable<IHierarchyDataItem> Model
        {
            get { return (IEnumerable<IHierarchyDataItem>)GetValue(ModelProperty); }
            set
            {
                this.SetValue(ModelProperty, value);
            }
        }

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);

            //if (e.Property.Name == ListView.ItemsSourceProperty.Name)
            if (e.Property.Name == TreeListView.ModelProperty.Name)
            {
                this.Root.Children.Clear();
                this.Rows.Clear();
                this.CreateChildrenNodes(this.Root);
            }
            else if (e.Property.Name == ListView.SelectedItemProperty.Name)
            {
                if (this.SelectedItem != null && this.SelectedItem is TreeListViewNode)
                {
                    this.SelectedObject = ((TreeListViewNode)this.SelectedItem).Tag;
                }
                else
                {
                    this.SelectedObject = null;
                }
            }
        }

        // Using a DependencyProperty as the backing store for Model.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ModelProperty = DependencyProperty.Register("Model", typeof(IEnumerable<IHierarchyDataItem>), typeof(TreeListView), new UIPropertyMetadata(null));

        public object SelectedObject
        {
            get { return (object)GetValue(SelectedObjectProperty); }
            set { SetValue(SelectedObjectProperty, value); }
        }

        // Using a DependencyProperty as the backing store for SelectedObject.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedObjectProperty = DependencyProperty.Register("SelectedObject", typeof(object), typeof(TreeListView));

        public TreeListViewNode Root { get; private set; }

        public ReadOnlyCollection<TreeListViewNode> Nodes
        {
            get { return this.Root.Nodes; }
        }

        internal TreeListViewNode PendingFocusNode
        {
            get;
            set;
        }

        public ICollection<TreeListViewNode> SelectedNodes
        {
            get
            {
                return SelectedItems.Cast<TreeListViewNode>().ToArray();
            }
        }

        public TreeListViewNode SelectedNode
        {
            get
            {
                if (this.SelectedItems.Count > 0)
                    return this.SelectedItems[0] as TreeListViewNode;
                else
                    return null;
            }
        }

        #endregion

        public TreeListView()
        {
            Rows = new ObservableCollectionAdv<TreeListViewNode>();
            this.Root = new TreeListViewNode(this, null);
            this.Root.IsExpanded = true;
            this.ItemsSource = Rows;
            this.ItemContainerGenerator.StatusChanged += ItemContainerGeneratorStatusChanged;
        }

        void ItemContainerGeneratorStatusChanged(object sender, EventArgs e)
        {
            if (ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated && PendingFocusNode != null)
            {
                var item = ItemContainerGenerator.ContainerFromItem(PendingFocusNode) as TreeListViewItem;
                if (item != null)
                    item.Focus();
                PendingFocusNode = null;
            }
        }

        protected override DependencyObject GetContainerForItemOverride()
        {
            return new TreeListViewItem();
        }

        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return item is TreeListViewItem;
        }

        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            var ti = element as TreeListViewItem;
            var node = item as TreeListViewNode;
            if (ti != null && node != null)
            {
                ti.Node = item as TreeListViewNode;
                base.PrepareContainerForItemOverride(element, node.Tag);
            }
        }

        internal void SetIsExpanded(TreeListViewNode node, bool value)
        {
            if (value)
            {
                if (!node.IsExpandedOnce)
                {
                    node.IsExpandedOnce = true;
                    node.AssignIsExpanded(value);
                    CreateChildrenNodes(node);
                }
                else
                {
                    node.AssignIsExpanded(value);
                    CreateChildrenRows(node);
                }
            }
            else
            {
                DropChildrenRows(node, false);
                node.AssignIsExpanded(value);
            }
        }

        internal void CreateChildrenNodes(TreeListViewNode node)
        {
            IEnumerable<IHierarchyDataItem> children = this.Model;
            if (node.Tag != null && node.Tag is IHierarchyDataItem)
            {
                //  Get children nodes 
                children = ((IHierarchyDataItem)node.Tag).Children;
            }
            //else
            //{
            //    children = null;
            //}

            //var children = GetChildren(node);
            if (children != null)
            {
                int rowIndex = this.Rows.IndexOf(node);
                node.ChildrenSource = children as INotifyCollectionChanged;
                foreach (IHierarchyDataItem obj in children)
                {
                    TreeListViewNode child = new TreeListViewNode(this, obj);
                    child.HasChildren = obj.HasChildren;
                    node.Children.Add(child);
                }
                this.Rows.InsertRange(rowIndex + 1, node.Children.ToArray());
            }
        }

        private void CreateChildrenRows(TreeListViewNode node)
        {
            int index = Rows.IndexOf(node);
            if (index >= 0 || node == this.Root) // ignore invisible nodes
            {
                var nodes = node.AllVisibleChildren.ToArray();
                Rows.InsertRange(index + 1, nodes);
            }
        }

        internal void DropChildrenRows(TreeListViewNode node, bool removeParent)
        {
            int start = Rows.IndexOf(node);
            if (start >= 0 || node == this.Root) // ignore invisible nodes
            {
                int count = node.VisibleChildrenCount;
                if (removeParent)
                    count++;
                else
                    start++;
                Rows.RemoveRange(start, count);
            }
        }

        private IEnumerable GetChildren(TreeListViewNode parent)
        {
            if (parent.Tag is IHierarchyDataItem)
            {
                return ((IHierarchyDataItem)parent.Tag).Children;
            }
            else
            {
                return null;
            }
        }

        private bool HasChildren(TreeListViewNode parent)
        {
            if (parent.Tag is IHierarchyDataItem)
            {
                return ((IHierarchyDataItem)parent.Tag).HasChildren;
            }
            else
            {
                return false;
            }
        }

        internal void InsertNewNode(TreeListViewNode parent, object tag, int rowIndex, int index)
        {
            TreeListViewNode node = new TreeListViewNode(this, tag);
            if (index >= 0 && index < parent.Children.Count)
                parent.Children.Insert(index, node);
            else
            {
                index = parent.Children.Count;
                parent.Children.Add(node);
            }
            Rows.Insert(rowIndex + index + 1, node);
        }
    }
}
